﻿// News Framework
// (C) Tanzim Saqib. Oct 25, 2010.
// http://TanzimSaqib.com | http://newsfx.codeplex.com

namespace TanzimSaqib.NewsFramework
{
    using System.Linq;
    using System.Xml.Linq;
    using System;
    using System.IO;
    using System.Net;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;

    public sealed class RssItem
    {
        public string Description { get; set; }
        public string Link { get; set; }
        public string Title { get; set; }
        public string ThumbnailURL { get; set; }
        public string Content { get; set; }
    }

    internal sealed class RssResponse
    {
        public List<RssItem> Items { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string URL { get; set; }
        public Exception Error { get; set; }
    }

    internal sealed class RssResponseCallback
    {
        public string URL { get; set; }
        public HttpWebRequest Request { get; set; }
        public int Count { get; set; }
        public Action<RssResponse> Callback { get; set; }
        public IRssItemParser ExtendedParser { get; set; }
    }

    public class RssExtendedParser : IRssItemParser
    {
        public Func<XElement, string> ThumbnailParser { get; set; }
        public Func<XElement, string> ContentParser { get; set; }
    }

    internal sealed class RssClient
    {
        private static readonly Regex _StripTagEx = new Regex("</?[^>]+>", RegexOptions.Compiled);

        private static string StripTags(string html, int trimAt)
        {
            string plainText = _StripTagEx.Replace(html, string.Empty);
            return plainText.Substring(0, Math.Min(plainText.Length, trimAt));
        }

        private static string StripTags(string html)
        {
            return _StripTagEx.Replace(html, string.Empty);
        }

        public static void FetchRssCallback(IAsyncResult result)
        {
            Exception error = null;
            List<RssItem> items = null;
            var title = string.Empty;
            var description = string.Empty;

            RssResponseCallback state = null;
            WebResponse response;

            try
            {
                state = result.AsyncState as RssResponseCallback;
                response = state.Request.EndGetResponse(result);

                using (var reader = new StreamReader(response.GetResponseStream()))
                {
                    var feed = XElement.Load(reader);

                    if (feed == null || state.Count < 1)
                    {
                        state.Callback(null);
                        return;
                    }

                    // RSS
                    if (feed.Element("channel") != null)
                    {
                        items = (from item in feed.Element("channel").Elements("item")
                                 select new RssItem
                                 {
                                     Title = StripTags(item.Element("title").Value, 200),
                                     Link = item.Element("link").Value,
                                     Description = item.Element("description") == null ? string.Empty : StripTags(item.Element("description").Value),
                                     ThumbnailURL = state.ExtendedParser.ThumbnailParser(item),
                                     Content = state.ExtendedParser.ContentParser(item)
                                 }).Take(state.Count).ToList();

                        title = feed.Element("channel").Element("title").Value;
                        description = feed.Element("channel").Element("description").Value;
                    }
                }
            }
            catch (Exception e)
            {
                error = e;
            }
            finally
            {
                state.Callback(new RssResponse() { URL = state.URL, Items = items, Description = description, Title = title, Error = error });
            }
            
        }

        internal static void FetchRssAsync(string url, int count, IRssItemParser extendedParser, Action<RssResponse> callback)
        {
            var request = WebRequest.Create(url) as HttpWebRequest;
            request.Method = "GET";

            request.BeginGetResponse(FetchRssCallback, new RssResponseCallback() { URL = url, Request = request, Count = count, ExtendedParser = extendedParser, Callback = callback });  
        }
    }
}
